home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Ham Radio 2000 #2
/
Ham Radio 2000 - Volume 2.iso
/
HAMV2
/
TCP_IP
/
TNOS230S
/
TCPUSER.C
< prev
next >
Wrap
C/C++ Source or Header
|
1996-12-23
|
10KB
|
428 lines
/* User calls to TCP
* Copyright 1991 Phil Karn, KA9Q
*/
#include "global.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "internet.h"
#include "iface.h"
#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: tcpuser.c,v 1.13 1996/12/23 22:44:37 root Exp root $";
#endif
int16 Tcp_window = DEF_WND;
/* open a tcp connection */
struct tcb *
open_tcp (lsocket, fsocket, mode, thewindow, r_upcall, t_upcall, s_upcall, tos, user)
struct socket *lsocket; /* Local socket */
struct socket *fsocket; /* Remote socket */
int mode; /* Active/passive/server */
int16 thewindow; /* Receive window (and send buffer) sizes */
void (*r_upcall) (struct tcb *, int16); /* Function to call when data arrives */
void (*t_upcall) (struct tcb *, int16); /* Function to call when ok to send more data */
void (*s_upcall) (struct tcb *, int, int); /* Function to call when connection state changes */
int tos;
int user; /* User linkage area */
{
struct connection conn;
register struct tcb *tcb;
struct iface *ifp = NULL;
struct route *rp;
if (lsocket == NULLSOCK) {
Net_error = INVALID;
return NULLTCB;
}
conn.local.address = lsocket->address;
conn.local.port = lsocket->port;
if (fsocket != NULLSOCK) {
conn.remote.address = fsocket->address;
conn.remote.port = fsocket->port;
} else {
conn.remote.address = 0;
conn.remote.port = 0;
}
if ((tcb = lookup_tcb (&conn)) == NULLTCB) {
/* If we are about to start a new connection,
* but don't have a route, disallow it ! - WG7J
*/
if (mode == TCP_ACTIVE) {
/* Is this to ourself ? */
if ((ifp = ismyaddr (conn.remote.address)) == NULL) {
/* No, do we have a route ? */
if ((rp = rt_lookup (conn.remote.address)) == NULL) {
/* We have no route to this system ! */
Net_error = NOROUTE;
return NULLTCB;
} else
ifp = rp->iface;
}
}
if ((tcb = create_tcb (&conn)) == NULLTCB) {
Net_error = NO_MEM;
return NULLTCB;
}
} else { /* if(tcb->state != TCP_LISTEN){
* prevent multiple servers being added to the
* server list in socket.c ! - WG7J
* Note that this bug still exists in vanilla KA9Q */
Net_error = CON_EXISTS;
return NULLTCB;
}
tcb->user = user;
if (thewindow != 0)
tcb->window = tcb->rcv.wnd = thewindow;
else {
if (ifp)
tcb->window = tcb->rcv.wnd = ifp->tcp->window;
else
tcb->window = tcb->rcv.wnd = Tcp_window;
}
tcb->snd.wnd = 1; /* Allow space for sending a SYN */
tcb->r_upcall = r_upcall;
tcb->t_upcall = t_upcall;
tcb->s_upcall = s_upcall;
tcb->tos = (char) tos;
switch (mode) {
case TCP_SERVER:
tcb->flags.clone = 1; /*lint !e616 Note fall-thru */
case TCP_PASSIVE:
/* Point to the default tcp parameters */
tcb->parms = &def_iftcp;
setstate (tcb, TCP_LISTEN);
break;
case TCP_ACTIVE:
/* We already have a route, found earlier.
* Point to this interface's tcp parameters - WG7J
*/
tcb->parms = (ifp) ? ifp->tcp : 0;
/* Set the interface specific mss values - WG7J */
tcb->cwind = tcb->mss = tcb->parms->mss;
/* Find a known rtt or load interface default - WG7J */
set_irtt (tcb);
/* Send SYN, go into TCP_SYN_SENT state */
tcb->flags.active = 1;
send_syn (tcb);
setstate (tcb, TCP_SYN_SENT);
tcp_output (tcb);
break;
default:
break;
}
return tcb;
}
/* User send routine */
int
send_tcp (tcb, bp)
register struct tcb *tcb;
struct mbuf *bp;
{
int16 cnt;
if (tcb == NULLTCB || bp == NULLBUF) {
free_p (bp);
Net_error = INVALID;
return -1;
}
cnt = len_p (bp);
switch (tcb->state) {
case TCP_CLOSED:
free_p (bp);
Net_error = NO_CONN;
return -1;
case TCP_LISTEN:
if (tcb->conn.remote.address == 0 && tcb->conn.remote.port == 0) {
/* Save data for later */
append (&tcb->sndq, bp);
tcb->sndcnt += cnt;
break;
}
/* Change state from passive to active */
tcb->flags.active = 1;
send_syn (tcb);
setstate (tcb, TCP_SYN_SENT); /*lint !e616 Note fall-thru */
case TCP_SYN_SENT:
case TCP_SYN_RECEIVED:
case TCP_ESTABLISHED:
case TCP_CLOSE_WAIT:
append (&tcb->sndq, bp);
tcb->sndcnt += cnt;
tcp_output (tcb);
break;
case TCP_FINWAIT1:
case TCP_FINWAIT2:
case TCP_CLOSING:
case TCP_LAST_ACK:
case TCP_TIME_WAIT:
free_p (bp);
Net_error = CON_CLOS;
return -1;
default:
break;
}
return (int) cnt;
}
/* User receive routine */
int
recv_tcp (tcb, bpp, cnt)
register struct tcb *tcb;
struct mbuf **bpp;
int16 cnt;
{
if (tcb == NULLTCB || bpp == (struct mbuf **) NULL) {
Net_error = INVALID;
return -1;
}
if (tcb->rcvcnt == 0) {
/* If there's nothing on the queue, our action depends on what state
* we're in (i.e., whether or not we're expecting any more data).
* If no more data is expected, then simply return 0; this is
* interpreted as "end of file". Otherwise return -1.
*/
switch (tcb->state) {
case TCP_LISTEN:
case TCP_SYN_SENT:
case TCP_SYN_RECEIVED:
case TCP_ESTABLISHED:
case TCP_FINWAIT1:
case TCP_FINWAIT2:
Net_error = WOULDBLK;
return -1;
case TCP_CLOSED:
case TCP_CLOSE_WAIT:
case TCP_CLOSING:
case TCP_LAST_ACK:
case TCP_TIME_WAIT:
*bpp = NULLBUF;
return 0;
default:
break;
}
}
/* cnt == 0 means "I want it all" */
if (cnt == 0)
cnt = tcb->rcvcnt;
/* See if the user can take all of it */
if (tcb->rcvcnt <= cnt) {
cnt = tcb->rcvcnt;
*bpp = tcb->rcvq;
tcb->rcvq = NULLBUF;
} else {
*bpp = ambufw (cnt);
(void) pullup (&tcb->rcvq, (*bpp)->data, cnt);
(*bpp)->cnt = cnt;
}
tcb->rcvcnt -= cnt;
tcb->rcv.wnd += cnt;
/* Do a window update if it was closed */
if (cnt == tcb->rcv.wnd) {
tcb->flags.force = 1;
tcp_output (tcb);
}
return (int) cnt;
}
/* This really means "I have no more data to send". It only closes the
* connection in one direction, and we can continue to receive data
* indefinitely.
*/
int
close_tcp (tcb)
register struct tcb *tcb;
{
if (tcb == NULLTCB) {
Net_error = INVALID;
return -1;
}
switch (tcb->state) {
case TCP_CLOSED:
return 0; /* Unlikely */
case TCP_LISTEN:
case TCP_SYN_SENT:
close_self (tcb, NORMAL);
return 0;
case TCP_SYN_RECEIVED:
case TCP_ESTABLISHED:
tcb->sndcnt++;
tcb->snd.nxt++;
setstate (tcb, TCP_FINWAIT1);
tcp_output (tcb);
return 0;
case TCP_CLOSE_WAIT:
tcb->sndcnt++;
tcb->snd.nxt++;
setstate (tcb, TCP_LAST_ACK);
tcp_output (tcb);
return 0;
case TCP_FINWAIT1:
case TCP_FINWAIT2:
case TCP_CLOSING:
case TCP_LAST_ACK:
case TCP_TIME_WAIT:
Net_error = CON_CLOS;
return -1;
default:
break;
}
return -1; /* "Can't happen" */
}
/* Delete TCB, free resources. The user is not notified, even if the TCB is
* not in the TCP_CLOSED state. This function should normally be called by the
* user only in response to a state change upcall to TCP_CLOSED state.
*/
int
del_tcp (conn)
struct tcb *conn;
{
register struct tcb *tcb;
struct tcb *tcblast = NULLTCB;
struct reseq *rp, *rp1;
/* Remove from list */
for (tcb = Tcbs; tcb != NULLTCB; tcblast = tcb, tcb = tcb->next)
if (tcb == conn)
break;
if (tcb == NULLTCB) {
Net_error = INVALID;
return -1; /* conn was NULL, or not on list */
}
if (tcblast != NULLTCB)
tcblast->next = tcb->next;
else
Tcbs = tcb->next; /* was first on list */
stop_timer (&tcb->timer);
for (rp = tcb->reseq; rp != NULLRESEQ; rp = rp1) {
rp1 = rp->next;
free_p (rp->bp);
free ((char *) rp);
}
tcb->reseq = NULLRESEQ;
free_p (tcb->rcvq);
free_p (tcb->sndq);
free ((char *) tcb);
return 0;
}
/* Return 1 if arg is a valid TCB, 0 otherwise */
int
tcpval (tcb)
struct tcb *tcb;
{
register struct tcb *tcb1;
if (tcb == NULLTCB)
return 0; /* Null pointer can't be valid */
for (tcb1 = Tcbs; tcb1 != NULLTCB; tcb1 = tcb1->next) {
if (tcb1 == tcb)
return 1;
}
return 0;
}
/* Kick a particular TCP connection */
int
kick_tcp (tcb)
register struct tcb *tcb;
{
if (!tcpval (tcb))
return -1;
tcb->flags.force = 1; /* Send ACK even if no data */
tcp_timeout (tcb);
return 0;
}
/* Kick all TCP connections to specified address; return number kicked */
int
kick (addr)
uint32 addr;
{
register struct tcb *tcb;
int cnt = 0;
for (tcb = Tcbs; tcb != NULLTCB; tcb = tcb->next) {
if (tcb->conn.remote.address == addr) {
(void) kick_tcp (tcb);
cnt++;
}
}
return cnt;
}
/* Clear all TCP connections */
void
reset_all ()
{
struct tcb *tcb, *tcb1;
for (tcb = Tcbs; tcb != NULLTCB; tcb = tcb1) {
tcb1 = tcb->next;
reset_tcp (tcb);
}
kwait (NULL); /* Let the RSTs go forth */
}
void
reset_tcp (tcb)
register struct tcb *tcb;
{
struct tcp fakeseg;
struct ip fakeip;
if (tcb == NULLTCB)
return;
if (tcb->state != TCP_LISTEN) {
/* Compose a fake segment with just enough info to generate the
* correct RST reply
*/
memset ((char *) &fakeseg, 0, sizeof (fakeseg));
memset ((char *) &fakeip, 0, sizeof (fakeip));
fakeseg.dest = tcb->conn.local.port;
fakeseg.source = tcb->conn.remote.port;
fakeseg.flags.ack = 1;
/* Here we try to pick a sequence number with the greatest likelihood
* of being in his receive window.
*/
fakeseg.ack = tcb->snd.nxt + tcb->snd.wnd - 1;
fakeip.dest = tcb->conn.local.address;
fakeip.source = tcb->conn.remote.address;
fakeip.tos = tcb->tos;
reset (&fakeip, &fakeseg);
}
close_self (tcb, RESET);
}
void
set_irtt (struct tcb *tcb)
{
struct tcp_rtt *tp;
if ((tp = rtt_get (tcb->conn.remote.address)) != NULLRTT) {
tcb->srtt = tp->srtt;
tcb->mdev = tp->mdev;
} else {
tcb->srtt = tcb->parms->irtt;
tcb->mdev = 0;
}
/* Initialize timer intervals */
set_timer (&tcb->timer, tcb->srtt);
}